package com.asset.manager.act;

import com.asset.exception.ThrowUtils;
import com.asset.manager.act.model.ActivitiProcessVal;
import com.asset.mapper.BudgetMapper;
import com.asset.model.entity.User;
import com.asset.model.vo.LoginUserVO;
import com.asset.utils.act.MyDefaultProcessDiagramGenerator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class BudgetActManager {
    @Resource
    private RuntimeService runtimeService;          // 运行时服务

    @Resource
    private TaskService taskService;                // 任务管理（任务的创建、查询、完成等操作）

    @Resource
    private IdentityService identityService;        // 组织机构和用户管理

    @Resource
    private RepositoryService repositoryService;    // 流程定义和部署对象管理

    @Resource
    private ProcessEngine processEngine;            // 流程引擎

    @Resource
    private HistoryService historyService;          // 历史服务(管理历史、日志信息

    @Resource
    private ActivitiProcessVal activitiProcessVal;  // 流程变量

    @Resource
    private BudgetMapper budgetMapper;

    private final String OPINION_AGREE = "同意";
    private final String OPINION_END = "否决";

    // 任务负责人（可以是申请人、审批人
    @Getter
    @Setter
    private String assignee = "";

    // 审批责任人
    @Getter
    @Setter
    private String leader = "";

    // 存储流程变量
    public Map<String, Object> variables = new HashMap<>();
    public Map<String, Object> globalVariable = new HashMap<>();

    public BudgetActManager() {
        variables.put("processVal", activitiProcessVal);
        globalVariable.put("assignee", assignee);   // 存入申请人
        globalVariable.put("leader", leader);       // 存入领导
    }

    /**
     * 启动流程实例（每次启动都会生成一个新的流程实例）
     *
     * @param user                    申请人
     * @param processDefinitionKey    流程定义key（对应processes文件夹下的xml文件）
     * @param piName
     */
    public String startProcess(LoginUserVO user, String processDefinitionKey, String piName) {
        // 设置预算流程申请人为提交预算申请的用户
        setAssignee(user.getUsername());
        // 设置公司领导(或上级审批人），用IsAdmin字段判断
        setLeader("2");

        // 获取流程定义
        ProcessDefinition pd = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .latestVersion()
                .singleResult();

        Map<String, Object> variables = new HashMap<>();
        variables.put("leader", user.getUsername()); // 将leader设置为当前申请人（或实际审批人）

        // 根据流程定义ID启动流程实例,同时往里设置全局变量
        ProcessInstance pi = runtimeService.startProcessInstanceByKey(pd.getKey(), this.globalVariable);

        // 设置流程实例名字（根据流程实例ID为流程设置名字，比如项目1预算申请）
        runtimeService.setProcessInstanceName(pi.getId(), piName);

        // 查看流程信息
        log.info("流程定义ID: {}", pi.getProcessDefinitionId());
        log.info("流程实例ID: {}", pi.getId());
        log.info("当前活动的ID: {}", pi.getActivityId());
        log.info("流程实例名字: {}", pi.getName());

        return pi.getId();
    }

    // 流程变量健康检查
    public void checkVariables(String pid) {
        Map<String, Object> variables = runtimeService.getVariables(pid);
        log.info("流程变量: {}", variables);
    }

    /**
     * 获取指定流程实例所有未完成的待办任务（需要处理的任务
     * 每个 Task 对象包含任务 ID、任务名称、负责人（或审批人）、流程实例等信息
     *
     * @param processInstanceId 流程实例ID
     * @return
     */
    public List<Task> getTasks(String processInstanceId) {
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .list();
    }

    /**
     * 获取指定用户需要处理的任务ID
     *
     * @param user              当前用户对象
     * @param processInstanceId 流程实例ID
     * @return
     */
    public String getTask(LoginUserVO user, String processInstanceId) {
        // 获取候选任务
        List<Task> todoList = getTaskTodoByUser(user);
        // 获取已领取任务
        List<Task> acceptList = getTaskAcceptByUser(user);
        // 合并两个任务列表
        List<Task> allTasks = new ArrayList<>();
        allTasks.addAll(todoList);
        allTasks.addAll(acceptList);

        log.info("用户 [{}] 的候选任务数量：{}，已领取任务数量：{}",
                user.getUsername(), allTasks.size(), acceptList.size());

        // 遍历后找到与目标流程实例 ID 匹配的任务，返回任务ID
        for (Task task : allTasks) {
            log.info("任务ID：{}，流程实例ID：{}，任务名称：{}",
                    task.getId(), task.getProcessInstanceId(), task.getName());
            if (processInstanceId.equals(task.getProcessInstanceId())) {
                log.info("找到匹配流程实例的任务ID：{}", task.getId());
                return task.getId();
            }
        }
        log.warn("用户 [{}] 在流程实例 [{}] 中未找到有效任务", user.getUsername(), processInstanceId);
        return null;
    }

    /**
     * 领取任务
     * 任务的领取是指将任务分配给指定的用户，使其成为该用户的待办任务。
     *
     * @param loginName 用户名
     * @param taskId    任务ID
     */
    public void collectTask(String loginName, String taskId) {
        taskService.claim(taskId, loginName);
    }

    // 根据用户获取代办（候选）的任务
    public List<Task> getTaskTodoByUser(LoginUserVO user) {
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(user.getPosition()).list();
        List<Task> tasks2 = taskService.createTaskQuery().taskCandidateUser(user.getUsername()).list();
        tasks.addAll(tasks2);
        return tasks;
    }
    // 根据用户获取受理（已领取）的任务
    public List<Task> getTaskAcceptByUser(LoginUserVO user) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(user.getUsername()).list();
        return tasks;
    }

    //
    /**
     * 输出指定用户在当前流程实例中已完成的任务信息
     * @param user              当前用户对象
     * @param processInstanceId 流程实例ID
     */
    public void getUserCompletedTasks(LoginUserVO user, String processInstanceId) {
        // 参数校验
        if (user == null || user.getUsername() == null || user.getUsername().trim().isEmpty()) {
            return;
        }
        if (processInstanceId == null || processInstanceId.trim().isEmpty()) {
            return;
        }

        // 使用历史服务查询指定用户在流程实例中的已完成任务
        List<HistoricTaskInstance> userCompletedTasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)   // 指定流程实例
                .finished()                             // 仅查询已完成的任务
                // 筛选条件：任务负责人是当前用户 或 任务候选用户包含当前用户
                .or()
                .taskAssignee(user.getUsername())
                .taskCandidateUser(user.getUsername())
                .endOr()
                .orderByTaskCreateTime().desc()         // 按任务创建时间倒序（可选）
                .list();

        if (userCompletedTasks.isEmpty()) {
            log.info("用户 [{}] 在流程实例 [{}] 中暂无已完成的任务", user.getUsername(), processInstanceId);
            return;
        }

        log.info("===== 用户 [{}] 在流程实例 [{}] 中的已完成任务列表 =====", user.getUsername(), processInstanceId);

        // 该任务的审批意见
        for (HistoricTaskInstance task : userCompletedTasks) {
            List<Comment> comments = taskService.getTaskComments(task.getId());
            if (comments != null && !comments.isEmpty()) {
                for (Comment comment : comments) {
                    // 输出评论信息
                    log.info("评论信息：{}" , comment.getFullMessage());
                }
            }
        }

        // 获取流程实例基本信息
        ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        List<String> activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
        for (HistoricTaskInstance task : userCompletedTasks) {
            log.info("流程ID: {}", pi.getId());
            log.info("任务ID: {}", task.getId());
            log.info("任务名称: {}", task.getName());
            log.info("任务定义键: {}", task.getTaskDefinitionKey());
            log.info("负责人: {}", task.getAssignee() != null ? task.getAssignee() : "无");
            log.info("开始时间: {}", task.getStartTime());
            log.info("完成时间: {}", task.getEndTime());
            log.info("耗时: {}ms", task.getDurationInMillis());
            log.info("任务状态: {}", task.getEndTime() != null ? "已完成" : "未完成");
            log.info("当前活动节点：{}", activeActivityIds);
        }
        // 2. 查询待办任务
        List<Task> allTodoTasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();
        allTodoTasks.forEach(task -> log.info("待办任务: {}", task.getName()));
    }

    /**
     * 完成当前任务
     * 参数：任务ID、流程实例ID、审批意见、预算金额、预算ID
     */
    public boolean completeTask(String taskId, String processInstanceId, String message, Double amount, int budgetId) {
        return completeComment(taskId, processInstanceId, message, amount, budgetId);
    }

    /**
     * 同意并完成当前任务（领导或上级公司部门）
     * 参数：任务ID、流程实例ID、审批意见、预算金额、预算ID
     */
    public boolean completeAgree(String taskId, String processInstanceId, String message, Double amount, int budgetId) {
        setMsgAgree();  // 同意
        return completeComment(taskId, processInstanceId, message, amount, budgetId);
    }

    /**
     * 不同意并完成任务（领导或上级公司部门）
     * 参数：任务ID、流程实例ID、审批意见、预算金额、预算ID
     */
    public boolean complete_disagree(String taskId, String processInstanceId, String message, Double amount, int budgetId) {
        setMsgEnd();    // 否决
        return completeComment(taskId, processInstanceId, message, amount, budgetId);
    }

    /**
     * 完成当前任务并添加备注
     * 参数：任务ID、流程实例ID、审批意见、预算金额、预算ID
     */
    private boolean completeComment(String taskId, String processInstanceId, String message, Double amount, int budgetId) {
        try {
            // 设置预算金额
            setAmount(amount);
            // 添加评论(备注)
            taskService.addComment(taskId, processInstanceId, message);
            taskService.setVariables(taskId, this.variables);
            // 完成当前任务（推进流程到下一节点
            taskService.complete(taskId);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // 检查流程是否结束
    public boolean isProcessEnd(String processInstanceId) {
        // 使用历史服务查询流程实例
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
               .processInstanceId(processInstanceId)
               .singleResult();
        // 检查流程实例是否存在
        if (historicProcessInstance == null) {
            return false;
        }
        // 检查流程实例是否已结束
        return historicProcessInstance.getEndTime() != null;
    }

    // 设置申请人
    private void setAssignee(String assignee) {
        this.assignee = assignee;
        globalVariable.replace("assignee", assignee);
    }

    // 设置审批人（领导
    private void setLeader(String leader) {
        this.leader = leader;
        globalVariable.replace("leader", leader);
    }

    // 设置流程变量
    // 同意
    private void setMsgAgree() {
        activitiProcessVal.setMsg(this.OPINION_AGREE);
        variables.replace("processVal", activitiProcessVal);
    }

    // 否决
    private void setMsgEnd() {
        activitiProcessVal.setMsg(this.OPINION_END);
        variables.replace("processVal", activitiProcessVal);
    }

    // 预算金额
    private void setAmount(Double amount) {
        activitiProcessVal.setAmount(amount);
        variables.replace("processVal", activitiProcessVal);
    }

    /**
     * 将流程图导出为PNG图片
     * @param processDefinitionId 流程定义ID
     * @param fileName 保存的文件名(不含扩展名)
     * @param saveToResources 是否保存到resources目录(仅开发环境可用)
     * @return 保存路径
     */
    public String exportProcessDiagramToPng(String processDefinitionId, String fileName, boolean saveToResources) {
        try {
            // 获取流程定义
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionId)
                    .singleResult();

            if (processDefinition == null) {
                log.error("流程定义ID {} 不存在", processDefinitionId);
                return null;
            }

            // 获取BPMN模型
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());

            // 生成流程图
            ProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
            InputStream imageStream = diagramGenerator.generateDiagram(
                    bpmnModel,
                    "png",
                    runtimeService.getActiveActivityIds(processDefinition.getId())
            );

            // 读取图片字节
            byte[] imageBytes = StreamUtils.copyToByteArray(imageStream);

            String outputPath;
            if (saveToResources) {
                // 开发环境: 保存到resources目录
                // 注意: 生产环境不建议这样做，因为resources目录通常打包后只读
                String resourcesPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath()
                        + "src/main/resources/static/process-images/";

                File directory = new File(resourcesPath);
                if (!directory.exists()) {
                    directory.mkdirs();
                }

                outputPath = resourcesPath + fileName + ".png";
                Files.write(new File(outputPath).toPath(), imageBytes);

                log.info("流程图已保存到resources目录: {}", outputPath);
            } else {
                // 生产环境: 保存到外部目录
                String externalDir = System.getProperty("user.dir") + "/process-images/";
                File directory = new File(externalDir);
                if (!directory.exists()) {
                    directory.mkdirs();
                }

                outputPath = externalDir + fileName + ".png";
                Files.write(new File(outputPath).toPath(), imageBytes);

                log.info("流程图已保存到外部目录: {}", outputPath);
            }

            return outputPath;
        } catch (Exception e) {
            log.error("导出流程图失败", e);
            return null;
        }
    }

    /**
     * 将流程实例的当前状态导出为PNG图片（不好用
     * 仅适用于开发环境，生产环境不建议使用
     *
     * @param processInstanceId 流程实例ID
     * @param fileName          保存的文件名(不含扩展名)
     * @param saveToResources   是否保存到resources目录(仅开发环境可用，在target查看)
     * @return 保存路径
     */
    public String exportCurrentProcessStateToPng(String processInstanceId, String fileName, boolean saveToResources) {
        try {
            // 获取流程定义ID
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();

            if (processInstance == null) {
                log.error("流程实例ID {} 不存在", processInstanceId);
                return null;
            }

            String processDefinitionId = processInstance.getProcessDefinitionId();

            // 获取BPMN模型
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

            // 获取当前活动节点ID
            List<String> activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);

            // 生成流程图
            ProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
            InputStream imageStream = diagramGenerator
                    .generateDiagram(bpmnModel,
                    "png",
                    activeActivityIds
            );

            // 读取图片字节
            byte[] imageBytes = StreamUtils.copyToByteArray(imageStream);

            String outputPath;
            if (saveToResources) {
                // 开发环境: 保存到resources目录
                String resourcesPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath()
                        + "src/main/resources/static/process-images/";

                File directory = new File(resourcesPath);
                if (!directory.exists()) {
                    directory.mkdirs();
                }

                outputPath = resourcesPath + fileName + ".png";
                Files.write(new File(outputPath).toPath(), imageBytes);

                log.info("流程实例当前状态图已保存到resources目录: {}", outputPath);
            } else {
                // 生产环境: 保存到外部目录
                String externalDir = System.getProperty("user.dir") + "/process-images/";
                File directory = new File(externalDir);
                if (!directory.exists()) {
                    directory.mkdirs();
                }

                outputPath = externalDir + fileName + ".png";
                Files.write(new File(outputPath).toPath(), imageBytes);

                log.info("流程实例当前状态图已保存到外部目录: {}", outputPath);
            }

            return outputPath;
        } catch (Exception e) {
            log.error("导出流程实例状态图失败", e);
            return null;
        }
    }

    /**
     * 获取流程图图片并保存到 resources 目录（老八用了说好吃！
     *
     * @param processInstanceId
     * @return
     */
    public String getActivitiImage(String processInstanceId) {
        try {
            // 1. 获取流程定义 ID
            String processDefinitionID = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .list()
                    .get(0)
                    .getProcessDefinitionId();

            // 2. 获取流程定义和 BPMN 模型
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionID)
                    .singleResult();
            BpmnModel model = repositoryService.getBpmnModel(pd.getId());
            org.activiti.bpmn.model.Process process = model.getMainProcess();

            // 3. 收集历史活动实例（按时间排序）
            List<HistoricActivityInstance> historicActivityInstanceList = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceStartTime()
                    .asc()
                    .list();

            // 4. 收集当前活动节点（用于高亮）
            List<String> current = new ArrayList<>();
            try {
                current = runtimeService.getActiveActivityIds(processInstanceId);
            } catch (Exception e) {
                log.error("流程已结束，无活动节点", e);
            }

            // 5. 推断执行路径（顺序流 ID）
            List<String> executedFlows = new ArrayList<>();
            for (int i = 0; i < historicActivityInstanceList.size() - 1; i++) {
                HistoricActivityInstance sourceActivity = historicActivityInstanceList.get(i);
                HistoricActivityInstance targetActivity = historicActivityInstanceList.get(i + 1);

                // 获取源节点（修复：使用 model 获取元素）
                FlowElement sourceElement = model.getFlowElement(sourceActivity.getActivityId());
                if (!(sourceElement instanceof FlowNode)) {
                    log.warn("活动ID [{}] 非节点类型，跳过", sourceActivity.getActivityId());
                    continue;
                }
                FlowNode sourceNode = (FlowNode) sourceElement;

                // 查找目标节点对应的顺序流
                for (SequenceFlow flow : sourceNode.getOutgoingFlows()) {
                    FlowElement targetElement = model.getFlowElement(flow.getTargetRef());
                    if (targetElement != null && targetElement.getId().equals(targetActivity.getActivityId())) {
                        executedFlows.add(flow.getId());
                        break;
                    }
                }
            }

            // 6. 生成流程图字节数组（使用自定义生成器）
            MyDefaultProcessDiagramGenerator generator = new MyDefaultProcessDiagramGenerator();
            String font = "宋体";     // 替换为系统可用中文字体
            InputStream imageStream = generator.generateDiagram(
                    model,
                    "png",
                    current,          // 高亮节点 ID
                    null,             // 低亮节点 ID（可选）
                    executedFlows,    // 执行的顺序流 ID
                    font, font, font, // 字体（节点/边/注释）
                    null,             // 类加载器（可选）
                    1.0               // 缩放比例（1.0 为原始大小）
            );

            // 7. 读取图片字节数组
            byte[] imageBytes = StreamUtils.copyToByteArray(imageStream);

            // 8. 保存到 resources 目录（static/process-images/）
            String resourcesPath = new ClassPathResource("").getFile().getAbsolutePath();
            Path targetDir = Paths.get(resourcesPath, "static", "process-images");
            if (!Files.exists(targetDir)) {
                Files.createDirectories(targetDir); // 创建目录（递归）
            }
            String fileName = "process_" + processInstanceId + ".png"; // 文件名格式：process_流程实例ID.png
            Path savePath = targetDir.resolve(fileName);
            Files.write(savePath, imageBytes);

            log.info("流程图已保存到 resources 目录: {}", savePath);
            return "static/process-images/" + fileName; // 返回相对路径（可通过 /static/process-images/... 访问）

        } catch (Exception e) {
            log.error("生成并保存流程图失败", e);
            return null;
        }
    }
}
